"""
The SettingManager is responsible to save and load all user specific settings.
"""

import sys, os, shutil
import cPickle
import xml.dom.minidom as minidom
from xml.sax.saxutils import escape, unescape
from symbide import LogManager

class Paths(dict):
    """
    There can be more than one path for a specific setting file - normally a 
    path to the users private setting file and another path to symbides default
    setting file.
    This class always returns only one path (the first). To get all paths use
    the "getAll" method.
    """
    def __getitem__(self, key):
        return dict.__getitem__(self, key)[0]
    
    def getAll(self, key):
        return dict.getitem__(self, key) 

# Important data for global use in Symbide
PATHS = Paths()
PROFILE = {}
SHORTCUTS = {}
OPTIONS = {}
LAYOUT = {}

class SettingManager:
    members = []
    settings = ('Paths', 'Profile', 'Shortcuts', 'Options', 'Layout')
    activeProfile = None
    
    def __init__(self):
        self.load()
    
    def inscribe(self, object):
        """
        If the settings are reloaded, the refresh method of all inscribed
        objects is called. This allows to apply changes whithout restarting
        Symbide.
        """
        self.members.append(object)
    
    def refresh(self):
        """
        Calls the refresh method of all inscribed objects.
        """
        for m in self.members:
            # Don't do any error checking here, if an object is inscribed it 
            # must have a refresh method.
            m.refresh()
    
    def load(self):
        """
        Load all settings.
        """
        for name in self.settings:
            getattr(self, 'load'+name)()
        self.refresh()
        
    def loadPaths(self, path=''):
        """
        Loads the paths in the "path.xml" file and stores them in PATHS.
        """
        if not path:
            path = self._resolvePath('paths.xml')
        global PATHS
        PATHS.clear()
        PATHS.update(self.getPaths(path))
        return PATHS
    
    def loadProfile(self, path=''):
        """
        Sets the default profile or, if path is given, the profile
        stored in path.
        """
        if not path:
            path = os.path.join(PATHS['profiles'], self._getDefault(PATHS['profiles']))
        global PROFILE
        PROFILE.clear()
        PROFILE.update(self.getProfile(path))
        return PROFILE
    
    def loadShortcuts(self, path=''):
        """
        Loads the shortcuts and saves them to SHORTCUTS. 
        """
        if not path:
            path = os.path.join(PATHS['shortcuts'], PROFILE['shortcuts'])
        global SHORTCUTS
        SHORTCUTS.clear()
        SHORTCUTS.update(self.getShortcuts(path))
        return SHORTCUTS
        
    def loadOptions(self, path=''):
        """
        Loads the options and saves them to OPTIONS. 
        """
        if not path:
            path = os.path.join(PATHS['options'], PROFILE['options'])
        global OPTIONS
        OPTIONS.clear()
        OPTIONS.update(self.getOptions(path))
        return OPTIONS
    
    def loadLayout(self, path=''):
        """
        Loads layout and saves it to LAYOUT. 
        """
        if not path:
            path = os.path.join(PATHS['layout'], PROFILE['layout'])
        global LAYOUT
        LAYOUT.clear()
        LAYOUT.update(self.getOptions(path))
        return LAYOUT
    
    def _resolvePath(self, path, dir=''):
        """
        Converts relative Paths to absolute paths.
        """
        cwd = os.getcwd() 
        if dir:
            os.chdir(dir)
        else:
            os.chdir(os.path.dirname(os.path.abspath(__file__)))
        path = os.path.abspath(str(path))
        os.chdir(cwd)
        return path
          
    def _getDefault(self, dir):
        """
        Returns the default file, that is set in a file named "default.txt".
        """
        f = open(os.path.join(dir,"default.txt"))
        buf = f.read().strip(" \t\n")
        f.close()
        return buf
    
    def getFiles(self, dir):
        """
        Returns the path of all xml files that are in the directory.
        """
        settingFiles = []
        paths = os.listdir(dir)
        for p in paths:
            longp = os.path.join(dir, p)
            if os.path.isfile(longp) and os.path.splitext(p)[1]==".xml":
                settingFiles.append(p)
        return settingFiles
    
    def getdom(self, path):
        """
        Returns the DOM representation of a setting file.
        """
        #TODO: test for validity
        DOM = minidom.parse(path)
        return DOM
    
    def savedom(self, DOM, path):
        """
        Saves a DOM-object to path.
        """
        f = open(path, 'w')
        f.write(DOM.toxml())
        f.close()
    
    def getFromPath(self, path):
        """
        Loads a xml-setting document from path. Depending on the type of the
        document it is further processed.
        """
        path = self._resolvePath(path)
        DOM = self.getdom(path)
        mainElement = DOM.documentElement
        if mainElement.localName.capitalize() in self.settings:
            buf = eval('self.get'+mainElement.localName.capitalize())(mainElement)
            return buf
    
    def _ensureUserPath(self, path, copy=True):
        """
        Make sure the given path exists. This is important to ensure that the
        user specific .symbide directory is set up propperly. 
        """
        user = os.path.expanduser('~')
        # if "~" can't be expanded return
        if user=='~':
            return
            
        userpath = os.path.normpath(os.path.join(user, '.symbide', path))
        
        if not os.path.exists(userpath):
            try:
                os.makedirs(userpath)
            except:
                LogManager.alert('can not create %s'%userpath)
                return
            if copy:
                copypath = self._resolvePath(path)
                for i in os.listdir(copypath):
                    if os.path.isfile(os.path.join(copypath, i)):
                        shutil.copy(os.path.join(copypath, i), os.path.join(userpath, i)) 
        return userpath
    
    def getPaths(self, path):
        """
        Returns all paths stored at given path.
        """
        DOM = self.getdom(path)
        mainElement = DOM.documentElement
        
        #create .symbide in users home-dir, if it doesn't exist
        self._ensureUserPath('', copy=False) 
        
        paths = {}
        for element in mainElement.childNodes:
            if element.nodeType is not element.ELEMENT_NODE:
                continue
            for node in element.childNodes:
                if node.nodeType is not node.TEXT_NODE:
                    continue
                mode = element.getAttribute('user')
                buf = node.data.strip(' \t\n')
                if mode=='False':
                    paths[element.localName] = [self._resolvePath(buf)]
                elif mode=='True':
                    paths[element.localName] = []
                    userpath = self._ensureUserPath(buf)
                    if userpath:
                        paths[element.localName].append(userpath)
                    paths[element.localName].append(self._resolvePath(buf))
                else:
                    raise
                
        return paths
    
    def getProfile(self, path):
        '''
        Returns profile stored at given path.
        '''
        DOM = self.getdom(path)
        mainElement = DOM.documentElement
        profile = {}
        for element in mainElement.childNodes:
            if element.nodeType is not element.ELEMENT_NODE:
                continue
            for node in element.childNodes:
                if node.nodeType is not node.TEXT_NODE:
                    continue
                profile[element.localName] = node.data.strip(' \t\n')
        return profile
    
    def getShortcuts(self, path):
        '''
        Returns all shortcuts stored at given path.
        '''
        from symbide import ModuleManager
        if not ModuleManager.hasModule('gtk'):
            LogManager.alert('need gtk for parsing shortcuts')
            return
        import gtk
        DOM = self.getdom(path)
        mainElement = DOM.documentElement
        shortcuts = {}
        for element in mainElement.childNodes:
            if element.nodeType != element.ELEMENT_NODE:
                continue
            keyval, modifier = None, None
            flag = True
            for node in element.childNodes:
                if node.nodeType is not node.TEXT_NODE:
                    continue
                buf = unescape(node.data).strip(' \t\n')
                if buf:
                    flag = False
                    shortcuts[element.localName] = (gtk.accelerator_parse(buf))
            if flag:
                shortcuts[element.localName] = (0,0)
        return shortcuts
    
    def getOptions(self, path):
        '''
        Returns all options stored at given path.
        '''
        DOM = self.getdom(path)
        mainElement = DOM.documentElement
        options = {}
        for element in mainElement.childNodes:
            if element.nodeType != element.ELEMENT_NODE:
                continue
            for text in element.childNodes:
                if text.nodeType != text.TEXT_NODE:
                    continue
                options[element.localName] = text.data.strip(' \n\t')
        return options
    
    def getLayout(self, path):
        '''
        Returns all layout quantities stored at given path.
        TODO: implement load layout-settings
        '''
        pass
